﻿using System;
using System.Data;
using System.Data.Common;
using FirebirdSql.Data.FirebirdClient;
using NHibernate.Driver;
using NHibernate.SqlCommand;
using NHibernate.SqlTypes;

namespace RG.Common.Provider
{
    public class NoWaitFirebirdClientDriver : FirebirdClientDriver
    {
        public override IDbConnection CreateConnection()
        {
            return new NoWaitFbConnection((FbConnection)base.CreateConnection());
        }

        public override IDbCommand GenerateCommand(CommandType type, SqlString sqlString, SqlType[] parameterTypes)
        {
            return new NoWaitDbCommand((FbCommand)base.GenerateCommand(type, sqlString, parameterTypes));
        }
    }

    public class NoWaitFbConnection : DbConnection, ICloneable, IDbConnection
    {
        public readonly FbConnection db;

        private static FbTransactionBehavior GetBehavior(IsolationLevel il)
        {
            switch (il)
            {
                case IsolationLevel.Snapshot:
                    return FbTransactionBehavior.NoWait | FbTransactionBehavior.Concurrency;
                case IsolationLevel.ReadCommitted:
                    return FbTransactionBehavior.NoWait | FbTransactionBehavior.ReadCommitted | FbTransactionBehavior.RecVersion;
                //case IsolationLevel.Unspecified:
                //case IsolationLevel.Chaos:
                //case IsolationLevel.Serializable:
                //case IsolationLevel.ReadUncommitted:
                //case IsolationLevel.RepeatableRead:
                default:
                    throw new NotImplementedException();
            }
        }

        public NoWaitFbConnection(FbConnection _db)
        {
            db = _db;
        }

        public new void Dispose()
        {
            db.Dispose();
        }

        protected override DbTransaction BeginDbTransaction(IsolationLevel isolationLevel)
        {
            return db.BeginTransaction(isolationLevel);
        }

        public new IDbTransaction BeginTransaction()
        {
            return db.BeginTransaction(new FbTransactionOptions { TransactionBehavior = GetBehavior(IsolationLevel.Unspecified)});
        }

        public new IDbTransaction BeginTransaction(IsolationLevel il)
        {
            return db.BeginTransaction(new FbTransactionOptions { TransactionBehavior = GetBehavior(il) });
        }

        public override void Close()
        {
            db.Close();
        }

        public override void ChangeDatabase(string databaseName)
        {
            db.ChangeDatabase(databaseName);
        }

        public new IDbCommand CreateCommand()
        {
            return CreateDbCommand();
        }

        protected override DbCommand CreateDbCommand()
        {
            var cmd = db.CreateCommand();
            cmd.Connection = db;
            return cmd;
        }

        public override void Open()
        {
            db.Open();
        }

        public override string ConnectionString
        {
            get { return db.ConnectionString; }
            set { db.ConnectionString = value; }
        }

        public new int ConnectionTimeout
        {
            get { return db.ConnectionTimeout; }
        }

        public override string Database
        {
            get { return db.Database; }
        }

        public override string DataSource
        {
            get { return db.DataSource; }
        }

        public override string ServerVersion
        {
            get { return db.ServerVersion; }
        }

        public override ConnectionState State
        {
            get { return db.State; }
        }

        object ICloneable.Clone()
        {
            return new NoWaitFbConnection(new FbConnection(db.ConnectionString));
        }

        public static implicit operator FbConnection(NoWaitFbConnection f)
        {
            return f.db;
        }
    }

    public class NoWaitDbCommand : DbCommand, ICloneable
    {
        private FbCommand cm { get; set; }

        public NoWaitDbCommand(FbCommand _cm)
        {
            cm = _cm;
        }

        public override void Prepare()
        {
            cm.Prepare();
        }

        public override string CommandText
        {
            get { return cm.CommandText; }
            set { cm.CommandText = value; }
        }

        public override int CommandTimeout
        {
            get { return cm.CommandTimeout; }
            set { cm.CommandTimeout = value; }
        }

        public override CommandType CommandType
        {
            get { return cm.CommandType; }
            set { cm.CommandType = value; }
        }

        public override UpdateRowSource UpdatedRowSource
        {
            get { return cm.UpdatedRowSource; }
            set { cm.UpdatedRowSource = value; }
        }

        protected override DbConnection DbConnection
        {
            get { return cm.Connection; }
            set
            {
                var NoWaitConn = value as NoWaitFbConnection;
                if (NoWaitConn != null) cm.Connection = NoWaitConn.db;
                else cm.Connection = (FbConnection)value;
            }
        }

        public override void Cancel()
        {
            cm.Cancel();
        }

        protected override DbParameterCollection DbParameterCollection
        {
            get { return cm.Parameters; }
        }

        protected override DbTransaction DbTransaction
        {
            get { return cm.Transaction; }
            set { cm.Transaction = (FbTransaction)value; }
        }

        public override bool DesignTimeVisible
        {
            get { return cm.DesignTimeVisible; }
            set { cm.DesignTimeVisible = value; }
        }

        protected override DbParameter CreateDbParameter()
        {
            return cm.CreateParameter();
        }

        protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
        {
            return cm.ExecuteReader(behavior);
        }

        public override int ExecuteNonQuery()
        {
            return cm.ExecuteNonQuery();
        }

        public override object ExecuteScalar()
        {
            return cm.ExecuteScalar();
        }

        public object Clone()
        {
            return new NoWaitDbCommand((FbCommand)((ICloneable)cm).Clone());
        }
    }
}
